package com.docking.heylo.service.impl;

import cn.hutool.core.date.DateUtil;
import com.docking.heylo.ThreadContext;
import com.docking.heylo.dao.entity.*;
import com.docking.heylo.dao.mapper.UserAccountMapper;
import com.docking.heylo.dao.mapper.UserAccountRecordMapper;
import com.docking.heylo.dao.mapper.UserBankMapper;
import com.docking.heylo.dao.mapper.UserWithdrawalsRecordMapper;
import com.docking.heylo.enums.EAccountRecord;
import com.docking.heylo.enums.EOrderState;
import com.docking.heylo.enums.EPayMethod;
import com.docking.heylo.enums.EWithdraw;
import com.docking.heylo.exception.GeneralException;
import com.docking.heylo.service.IAccountService;
import com.docking.heylo.utils.HeyloMessageSource;
import com.docking.heylo.web.dto.UserBankDto;
import com.docking.heylo.web.dto.WithdrawDto;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class AccountServiceImpl implements IAccountService {

    @Resource
    private UserAccountMapper userAccountMapper;

    @Resource
    private UserAccountRecordMapper userAccountRecordMapper;

    @Resource
    private UserBankMapper userBankMapper;

    @Resource
    private UserWithdrawalsRecordMapper userWithdrawalsRecordMapper;

    @Autowired
    private HeyloMessageSource ms;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void openAccount(int userId) {
        userAccountMapper.insertSelective(new UserAccount(){{
            setId(userId);
            setBalance(BigDecimal.ZERO);
            setFrozenAmount(BigDecimal.ZERO);
            setVersion(randomNumber());
        }});
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void credit(int userId, BigDecimal amount, String remark, int businessId, String value1, EAccountRecord.BusinessYype businessType, EPayMethod payMethod, EOrderState orderState) {
        if(amount.doubleValue() <= 0){
            return;
        }

        UserAccount account = userAccountMapper.selectByPrimaryKey(userId);
        if(account != null){
            UserAccountExample example = new UserAccountExample();
            UserAccountExample.Criteria criteria = example.createCriteria();
            criteria.andVersionEqualTo(account.getVersion());
            criteria.andIdEqualTo(account.getId());

            userAccountMapper.updateByExampleSelective(new UserAccount(){{
                setBalance(account.getBalance().add(amount));
                setVersion(randomNumber());
            }},example);
        }

        // 创建支付记录
        insertAccountRecord(userId, amount, remark, businessId, value1, businessType, payMethod, orderState,
                account.getBalance(), account.getBalance().add(amount));
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void payOut(int userId, BigDecimal amount, String remark, int businessId, String value1,
                       EAccountRecord.BusinessYype businessType, EPayMethod payMethod, EOrderState orderState) {
        UserAccount account = userAccountMapper.selectByPrimaryKey(userId);

        // 余额支付
        if(account != null && payMethod.getValue() == EPayMethod.balance.getValue()){
            if(BigDecimal.ZERO.compareTo(account.getBalance().subtract(amount)) == 1){
                throw new GeneralException(ms.getMessage("insufficient.balance"));
            }

            UserAccountExample example = new UserAccountExample();
            UserAccountExample.Criteria criteria = example.createCriteria();
            criteria.andVersionEqualTo(account.getVersion());
            criteria.andIdEqualTo(account.getId());

            if(account.getBalance().subtract(amount).doubleValue() < 0){
                throw new GeneralException("账户余额不足");
            }

            userAccountMapper.updateByExampleSelective(new UserAccount(){{
                setBalance(account.getBalance().subtract(amount));
                setVersion(randomNumber());
            }},example);
        }

        insertAccountRecord(userId, amount, remark, businessId, value1, businessType, payMethod, orderState,
                account.getBalance(), account.getBalance().subtract(amount));
    }

    /***
     * 创建支付记录
     *
     * @param userId                用户ID
     * @param amount                金额
     * @param remark                描述
     * @param businessId            业务ID
     * @param value1                扩展字段
     * @param businessType          业务类型
     * @param payMethod             支付方式
     * @param orderState            订单状态
     * @param current               当前余额
     * @param changed               变更后余额
     */
    public void insertAccountRecord(int userId, BigDecimal amount, String remark, int businessId, String value1,
                                    EAccountRecord.BusinessYype businessType, EPayMethod payMethod, EOrderState orderState, BigDecimal current, BigDecimal changed){
        // 创建支付记录
        userAccountRecordMapper.insertSelective(new UserAccountRecord(){{
            setCreateAt(new Date());
            setChangeType(EAccountRecord.ChangeType.reduce.getValue().byteValue());
            setChangeAmount(amount);
            setChangeReason(remark);
            setBusinessId(businessId);
            setValue1(value1);
            setBusinessType(businessType.getValue());
            setMethod(payMethod.getValue());
            setCurrentBalance(current);
            setChangedBalance(changed);
            setStatus(orderState.getValue());
            setUserId(userId);
        }});
    }

    @Override
    public void updateStatus(int userId, String value1, EOrderState orderState) {
        UserAccountRecordExample example = new UserAccountRecordExample();
        UserAccountRecordExample.Criteria criteria = example.createCriteria();
        criteria.andValue1EqualTo(value1);
        criteria.andUserIdEqualTo(userId);
        userAccountRecordMapper.updateByExampleSelective(new UserAccountRecord(){{
            setStatus(orderState.getValue());
        }},example);
    }

    public String randomNumber(){
        return (Math.random() + "").substring(3,10);
    }


    @Override
    public Map<String, Object> info() {
        UserAccount account = userAccountMapper.selectByPrimaryKey(ThreadContext.userId());
        return new HashMap<String, Object>(){{
            put("balance", account.getBalance());
        }};
    }

    @Override
    public List<UserBank> userBanks() {
        UserBankExample example = new UserBankExample();
        UserBankExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(ThreadContext.userId());
        return userBankMapper.selectByExample(example);
    }

    @Override
    public void saveBank(UserBankDto userBankDto) {
        UserBankExample example = new UserBankExample();
        UserBankExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(ThreadContext.userId());
        if(userBankMapper.countByExample(example) > 5){
            throw new GeneralException(ms.getMessage("bank_card.max"));
        }

        userBankMapper.insertSelective(new UserBank(){{
            setBankName(userBankDto.getBankName());
            setCardNumber(userBankDto.getCardNumber());
            setCardOwner(userBankDto.getCardOwner());
            setPhone(userBankDto.getPhone());
            setCrtAt(new Date());
            setUserId(ThreadContext.userId());
        }});
    }

    @Override
    public void removeBankCard(UserBank userBank) {
        UserBankExample example = new UserBankExample();
        UserBankExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(ThreadContext.userId());
        criteria.andIdEqualTo(userBank.getId());
        userBankMapper.deleteByExample(example);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void withdraw(WithdrawDto withdrawDto) {
        if(withdrawDto.getAmount().compareTo(BigDecimal.ZERO) != 0){
            throw new GeneralException("请输入有效提现金额");
        }

        UserBank userBank = userBankMapper.selectByPrimaryKey(withdrawDto.getBankCardId());

        // steep1 创建提现记录
        UserWithdrawalsRecord record = new UserWithdrawalsRecord(){{
            setUserId(ThreadContext.userId());
            setBankName(userBank.getBankName());
            setBankCard(userBank.getCardNumber());
            setCardOwner(userBank.getCardOwner().trim());
            setCrtAt(new Date());
            setAmount(withdrawDto.getAmount());
            setHandlingFee(BigDecimal.ZERO);
            setStatus(EWithdraw.Status.pending_payment.getValue());
        }};
        userWithdrawalsRecordMapper.insertSelective(record);

        // steep2 扣除账户余额
        payOut(record.getUserId(), withdrawDto.getAmount(), "提现", record.getId(),
                null,EAccountRecord.BusinessYype.withdraw, EPayMethod.balance, EOrderState.create
        );

    }

    @Override
    public PageInfo<Map<String, Object>> historyRecord(WithdrawDto withdrawDto) {
        PageHelper.startPage(withdrawDto.getPageNumber(), withdrawDto.getSize());

        if(withdrawDto.getBeginTime() != null){
            withdrawDto.setBeginTime(DateUtil.beginOfDay(withdrawDto.getBeginTime()));
        }
        if(withdrawDto.getEndTime() != null){
            withdrawDto.setEndTime(DateUtil.endOfDay(withdrawDto.getEndTime()));
        }

        return new PageInfo<>(userAccountRecordMapper.selectHistoryByUserId(ThreadContext.userId(),
                withdrawDto.getBusinessType(), withdrawDto.getStatus(), withdrawDto.getBeginTime(), withdrawDto.getEndTime()));
    }



}
